package com.fx.spring;

import com.fx.spring.annotation.SpringFX;
import com.fx.spring.constant.SpringConstantFX;
import com.fx.spring.context.ApplicationContextFX;
import com.fx.spring.jfx.AbstractSpringStartFX;
import com.fx.spring.jfx.ApplicationStageFX;
import javafx.application.Application;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.util.ClassUtils;

import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Map;

/**
 * 为JavaFX 提供的简单 Spring 环境
 * 使 JavaFX 完全被Spring环境所托管
 * 设计理念为SpringBoot 启动方式
 * <p>
 * 1.初始化@SpringFX 启动类，获取SpringFX的配置信息
 * 2.加载配置文件设置vm参数
 * 3.初始化SpringApplicationContext
 * 4.初始化 ApplicationFX上下文
 * 5.启动 javafx
 */
public class SpringApplicationFX {

    private static AnnotationConfigApplicationContext applicationContext;
    private static SpringFX springFXAnn;
    private static Logger logger;
    private static long startTime;

    /**
     * 运行方法
     *
     * @param primarySource 主要资源类（启动类 ，被@SpringFX注解的类）
     * @param args          程序运行参数
     */
    public static void run(Class<?> primarySource, String... args) {
        //1.初始化@SpringFX 启动类，获取SpringFX的配置信息
        initSpringFXAnnotation(primarySource);
        //2.加载配置文件设置vm参数
        //initConfig();
        //3.加载Sl4j
        initLog(primarySource);
        //4.初始化SpringApplicationContext
        initSpringApplicationContext(primarySource);
        //5.初始化 fx上下文
        initApplicationContextFX();
        //6.启动 javafx
        startApplicationFXStage(args);
    }

    /**
     * 初始化被@SpringFX注解类 获取主类@SpringFX注解配置信息
     *
     * @param primarySource 主启动类
     */
    private static void initSpringFXAnnotation(Class<?> primarySource) {
        startTime = System.currentTimeMillis();
        if (primarySource.isAnnotationPresent(SpringFX.class)) {
            springFXAnn = primarySource.getAnnotation(SpringFX.class);
        } else {
            throw new RuntimeException("未添加" + ClassUtils.classNamesToString(SpringFX.class) + "注解");
        }
    }

    /**
     * 初始化配置信息，获取配置文件，init.properties中的参数 添加到vm中
     */
    /*private static void initConfig() {
        String filePath = springFXAnn.initFilePath();
        try {
            File file = new File(filePath);
            if (file.exists()) {
                Properties properties = new Properties();
                properties.load(ClassLoader.getSystemResourceAsStream(filePath));
                if (!properties.isEmpty()) {
                    for (Map.Entry<Object, Object> property : properties.entrySet()) {
                        System.setProperty((String) property.getKey(), (String) property.getValue());
                    }
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(filePath + "==》配置文件加载异常");
        }
    }*/

    /**
     * 在参数初始化完成后加载sl4j,因为参数中可能包含所需要的参数
     */
    private static void initLog(Class<?> primarySource) {
        logger = LoggerFactory.getLogger(SpringApplicationFX.class);
        logger.debug("启动主类和主配置类[{}]", ClassUtils.classNamesToString(primarySource));
    }

    /**
     * 初始化Spring环境及配置信息
     *
     * @param primarySource 配置类，被@SpringFX注解的启动类就是一个配置类
     */
    private static void initSpringApplicationContext(Class<?> primarySource) {
        applicationContext = new AnnotationConfigApplicationContext(primarySource);
        String packageName = ClassUtils.getPackageName(primarySource);
        logger.debug("Spring 环境默认扫描包路径，[{}]", packageName);
        applicationContext.scan(packageName);
        logger.debug("Spring 环境启动完成，[{}]", ClassUtils.classNamesToString(applicationContext.getClass()));
    }

    /**
     * 在初始化完成Spring后，设置JavaFX上下文
     */
    private static void initApplicationContextFX() {
        ApplicationContextFX contextFX = applicationContext.getBean(ApplicationContextFX.class);
        contextFX.setStartTime(startTime);
        logger.debug("获取Spring容器中的FX上下文[{}]", contextFX);
        Class<? extends Annotation> startAnnotation = springFXAnn.startAnnotation();
        logger.debug("JavaFX启动注解[{}]", ClassUtils.classNamesToString(startAnnotation));
        Map<String, Object> beansWithStartFX = applicationContext.getBeansWithAnnotation(startAnnotation);
        if (beansWithStartFX.isEmpty()) {
            logger.error("没有被[{}]注释的类", ClassUtils.classNamesToString(startAnnotation));
            throw new RuntimeException();
        }
        AbstractSpringStartFX startFXClass = null;
        for (Map.Entry<String, Object> entry : beansWithStartFX.entrySet()) {
            Object object = entry.getValue();
            logger.debug("被[{}]注解的类[{}]", ClassUtils.classNamesToString(startAnnotation), ClassUtils.classNamesToString(object.getClass()));
            if (object instanceof AbstractSpringStartFX) {
                startFXClass = (AbstractSpringStartFX) object;
                break;
            }
        }
        if (startFXClass == null) {
            logger.error("没有实现[{}]的类", ClassUtils.classNamesToString(AbstractSpringStartFX.class));
            throw new RuntimeException();
        }
        logger.debug("最终使用的fx启动类[{}]", ClassUtils.classNamesToString(startFXClass.getClass()));
        contextFX.setStartClass(startFXClass);
    }

    /**
     * 启动ApplicationFX窗口
     *
     * @param args 启动参数
     */
    private static void startApplicationFXStage(String... args) {
        logger.debug("JavaFX启动开始，启动参数[{}]", Arrays.toString(args));
        Application.launch(ApplicationStageFX.class, args);
    }
}
